{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 이전 대화를 기억하는 Chain 생성방법"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# API KEY를 환경변수로 관리하기 위한 설정 파일\n",
    "from dotenv import load_dotenv\n",
    "\n",
    "# API KEY 정보로드\n",
    "load_dotenv()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# LangSmith 추적을 설정합니다. https://smith.langchain.com\n",
    "# !pip install langchain-teddynote\n",
    "from langchain_teddynote import logging\n",
    "\n",
    "# 프로젝트 이름을 입력합니다.\n",
    "logging.langsmith(\"CH05-Memory\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 이전 대화내용을 기억하는 multi-turn Chain"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n",
    "from langchain_community.chat_message_histories import ChatMessageHistory\n",
    "from langchain_core.chat_history import BaseChatMessageHistory\n",
    "from langchain_core.runnables.history import RunnableWithMessageHistory\n",
    "from langchain_openai import ChatOpenAI\n",
    "from langchain_core.output_parsers import StrOutputParser\n",
    "\n",
    "\n",
    "# 프롬프트 정의\n",
    "prompt = ChatPromptTemplate.from_messages(\n",
    "    [\n",
    "        (\n",
    "            \"system\",\n",
    "            \"당신은 Question-Answering 챗봇입니다. 주어진 질문에 대한 답변을 제공해주세요.\",\n",
    "        ),\n",
    "        # 대화기록용 key 인 chat_history 는 가급적 변경 없이 사용하세요!\n",
    "        MessagesPlaceholder(variable_name=\"chat_history\"),\n",
    "        (\"human\", \"#Question:\\n{question}\"),  # 사용자 입력을 변수로 사용\n",
    "    ]\n",
    ")\n",
    "\n",
    "# llm 생성\n",
    "llm = ChatOpenAI(model_name=\"gpt-4.1-mini\")\n",
    "\n",
    "# 일반 Chain 생성\n",
    "chain = prompt | llm | StrOutputParser()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "대화를 기록하는 체인 생성(`chain_with_history`)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 세션 기록을 저장할 딕셔너리\n",
    "store = {}\n",
    "\n",
    "\n",
    "# 세션 ID를 기반으로 세션 기록을 가져오는 함수\n",
    "def get_session_history(session_ids):\n",
    "    print(f\"[대화 세션ID]: {session_ids}\")\n",
    "    if session_ids not in store:  # 세션 ID가 store에 없는 경우\n",
    "        # 새로운 ChatMessageHistory 객체를 생성하여 store에 저장\n",
    "        store[session_ids] = ChatMessageHistory()\n",
    "    return store[session_ids]  # 해당 세션 ID에 대한 세션 기록 반환"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "chain_with_history = RunnableWithMessageHistory(\n",
    "    chain,\n",
    "    get_session_history,  # 세션 기록을 가져오는 함수\n",
    "    input_messages_key=\"question\",  # 사용자의 질문이 템플릿 변수에 들어갈 key\n",
    "    history_messages_key=\"chat_history\",  # 기록 메시지의 키\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "첫 번째 질문 실행"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "chain_with_history.invoke(\n",
    "    # 질문 입력\n",
    "    {\"question\": \"나의 이름은 테디입니다.\"},\n",
    "    # 세션 ID 기준으로 대화를 기록합니다.\n",
    "    config={\"configurable\": {\"session_id\": \"abc123\"}},\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "이어서 질문 실행"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "chain_with_history.invoke(\n",
    "    # 질문 입력\n",
    "    {\"question\": \"내 이름이 뭐라고?\"},\n",
    "    # 세션 ID 기준으로 대화를 기록합니다.\n",
    "    config={\"configurable\": {\"session_id\": \"abc123\"}},\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "아래는 `session_id`가 다른 경우 새로운 세션이 생성되는 경우입니다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "chain_with_history.invoke(\n",
    "    # 질문 입력\n",
    "    {\"question\": \"내 이름이 뭐라고?\"},\n",
    "    # 세션 ID 기준으로 대화를 기록합니다.\n",
    "    config={\"configurable\": {\"session_id\": \"abc1234\"}},\n",
    ")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "langchain-kr-lwwSZlnu-py3.11",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
